import java.net.*;
import java.io.*;
import java.util.*;

public class IterativeChatServer { 
  private ArrayList<ChatClientRecord> groupList;

  private class RegFailureException extends Exception { }

  private class ClientWatchdog extends Thread {
    public void run() {
      System.out.println("Client watchdog started");
      // Cleanup old clients
      while (true) {
        synchronized (groupList) {
          int size = groupList.size();
          for (int i = 0; i < size; i++) {
            ChatClientRecord rec = groupList.get(i);
            long currentTime = System.currentTimeMillis();
            if (currentTime - rec.lastHeartbeat > 60*1000) {
              System.out.println("Removing idle client " + rec.name);
              groupList.remove(i); 
              break;
            }
          }
        }
      }
    }
  }

  public IterativeChatServer(int serverPort) {
    // Keep a record of available clients
    groupList = new ArrayList<ChatClientRecord>();

    // Start up server
    ServerSocket serverSocket = null;
    try {
      serverSocket = new ServerSocket(serverPort);
      System.out.println("IterativeChatServer started on port " + serverPort);
    } catch (IOException e) {
      System.err.println("Could not listen on port: " + serverPort);
      System.exit(1);
    }

    // Start idle client cleanup
    ClientWatchdog watch = new ClientWatchdog();
    watch.start();

    // Accept chat client connections
    while (true) {
      // Accept a new connection
      Socket clientSocket = null;
      try {
        clientSocket = serverSocket.accept();
        System.out.println("Accepted connection from " 
            + clientSocket.getInetAddress().getHostName() 
            + ":" + clientSocket.getPort());
        ObjectInputStream in = new ObjectInputStream(clientSocket.getInputStream());
        ObjectOutputStream out = new ObjectOutputStream(clientSocket.getOutputStream());
        ChatMessage request = (ChatMessage)in.readObject();

        if (request.type == ChatMessage.REG_ATTEMPT) {
          // Attempt to register client
          ChatClientRecord rec = request.clientRec;
          String regName = rec.name;
          try {
            for (ChatClientRecord record : groupList)
              if (record.name.equals(regName))
                throw new RegFailureException();
            rec.lastHeartbeat = System.currentTimeMillis();
            synchronized (groupList) {
              groupList.add(rec);
            }
            ChatMessage msg = new ChatMessage(ChatMessage.REG_SUCCESS, null, null, null);
            out.writeObject(msg);
          } catch (RegFailureException e) {
            System.out.println("A client with name " + regName + " already exists.");
            ChatMessage msg = new ChatMessage(ChatMessage.REG_FAIL, null, null, null);
            out.writeObject(msg);
          }
        } else if (request.type == ChatMessage.HEARTBEAT) {
          // Update last heartbeat time to current time
          ChatClientRecord clientRec = request.clientRec;
          String name = clientRec.name;
          for (ChatClientRecord rec : groupList)
            if (rec.name.equals(name))
              rec.lastHeartbeat = System.currentTimeMillis();
        } else if (request.type == ChatMessage.GET_LIST) {
          // Send group list back to client
          ChatMessage msg = new ChatMessage(ChatMessage.GET_LIST, null, groupList, null);
          out.writeObject(msg);
          System.out.println("Sent list to client");
        }

        clientSocket.close();
      } catch (ClassNotFoundException e) {
        System.err.println("Could not find class ChatMessage");
        System.exit(1);
      } catch (IOException e) {
        System.err.println("Accept failed (IOException)");
      }
    }
  }

  public static void main(String[] args) {
    int serverPort;
    if (args.length == 1) {
      serverPort = Integer.parseInt(args[0]);
      new IterativeChatServer(serverPort);
    }else{
      System.err.println("Usage: java IterativeServer <serverPort>");
      System.exit(1);
    }
  }
}
